import akshare as ak
import pandas as pd
from datetime import datetime, timedelta
import numpy as np
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from data_cache import DataCache

class ValueStockAnalyzer:
    """格雷厄姆价值投资分析器"""
    
    def __init__(self):
        """初始化价值股分析器"""
        # 初始化数据缓存管理器
        self.cache = DataCache()
        # 初始化线程池
        self.executor = ThreadPoolExecutor(max_workers=10)
        
    def get_trade_date(self, offset=1):
        """获取交易日期，默认获取最近的一个交易日"""
        try:
            # 使用AKShare获取A股交易日历
            trade_cal = ak.tool_trade_date_hist_sina()
            # 转换为日期格式
            trade_cal['trade_date'] = pd.to_datetime(trade_cal['trade_date'])
            # 按日期降序排序
            trade_cal = trade_cal.sort_values('trade_date', ascending=False)
            
            # 获取交易日列表
            trade_dates = trade_cal['trade_date'].dt.strftime('%Y%m%d').tolist()
            
            # 返回指定偏移的交易日
            if offset < len(trade_dates):
                return trade_dates[offset]
            else:
                return trade_dates[-1]
        except Exception as e:
            # 如果出错，使用备用方法
            today = datetime.now()
            # 如果是周末，调整到周五
            if today.weekday() == 5:  # 星期六
                today = today - timedelta(days=1)
            elif today.weekday() == 6:  # 星期日
                today = today - timedelta(days=2)
                
            # 计算偏移日期
            target_date = today - timedelta(days=offset)
            # 如果是周末，调整到周五
            if target_date.weekday() == 5:  # 星期六
                target_date = target_date - timedelta(days=1)
            elif target_date.weekday() == 6:  # 星期日
                target_date = target_date - timedelta(days=2)
                
            return target_date.strftime('%Y%m%d')
    
    def get_all_stocks(self):
        """获取所有A股股票"""
        try:
            # 检查缓存
            cached_data = self.cache.get_cache('stock_list')
            if cached_data is not None:
                print("使用缓存的股票列表数据")
                return cached_data
            
            print("从API获取股票列表数据...")
            # 使用AKShare获取A股实时行情
            df = ak.stock_zh_a_spot_em()
            
            # 缓存数据
            if not df.empty:
                self.cache.set_cache(df, 'stock_list')
            
            return df
        except Exception as e:
            print(f"获取A股股票数据时出错: {str(e)}")
            return pd.DataFrame()
    
    def get_top_volume_stocks(self, top_n=200):
        """获取成交额前N名的股票"""
        try:
            # 获取所有A股股票
            df = self.get_all_stocks()
            if df.empty:
                return pd.DataFrame()
            
            # 确保成交额列存在
            if '成交额' not in df.columns:
                print("无法获取成交额数据")
                return pd.DataFrame()
            
            # 将成交额转换为数值类型
            df['成交额'] = pd.to_numeric(df['成交额'], errors='coerce')
            
            # 按成交额排序
            df = df.sort_values(by='成交额', ascending=False)
            return df.head(top_n)
        except Exception as e:
            print(f"获取成交额前N名股票时出错: {str(e)}")
            return pd.DataFrame()
    
    def get_stock_financial_info(self, stock_code):
        """获取单个股票的财务信息"""
        try:
            # 检查缓存
            cached_data = self.cache.get_cache('financial_info', stock_code)
            if cached_data is not None:
                return cached_data
            
            # 使用AKShare获取个股信息
            info = ak.stock_individual_info_em(symbol=stock_code)
            
            # 缓存数据
            if not info.empty:
                self.cache.set_cache(info, 'financial_info', stock_code)
            
            return info
        except Exception as e:
            print(f"获取股票{stock_code}财务信息时出错: {str(e)}")
            return pd.DataFrame()
    
    def check_value_stock(self, stock_code, row, pe_threshold=15, pb_threshold=1.5, div_yield_threshold=4):
        """检查股票是否为价值股并返回行数据"""
        try:
            if self.is_value_stock(stock_code, pe_threshold, pb_threshold, div_yield_threshold):
                return row
            return None
        except Exception as e:
            print(f"检查股票{stock_code}是否为价值股时出错: {str(e)}")
            return None
    
    def is_value_stock(self, stock_code, pe_threshold=15, pb_threshold=1.5, div_yield_threshold=4):
        """判断是否为价值股（按格雷厄姆标准）"""
        try:
            info = self.get_stock_financial_info(stock_code)
            if info.empty:
                return False
            
            # 提取关键财务指标
            try:
                pe_row = info[info['item'] == '市盈率(TTM)']['value'].iloc[0]
                pe = float(pe_row.replace('%', '') if isinstance(pe_row, str) else pe_row)
            except:
                pe = float('inf')  # 如果无法获取PE，设为无穷大
                
            try:
                pb_row = info[info['item'] == '市净率']['value'].iloc[0]
                pb = float(pb_row.replace('%', '') if isinstance(pb_row, str) else pb_row)
            except:
                pb = float('inf')  # 如果无法获取PB，设为无穷大
                
            try:
                div_row = info[info['item'] == '股息率(%)']['value'].iloc[0]
                div_yield = float(div_row.replace('%', '') if isinstance(div_row, str) else div_row)
            except:
                try:
                    div_row = info[info['item'] == '股息率(TTM)']['value'].iloc[0]
                    div_yield = float(div_row.replace('%', '') if isinstance(div_row, str) else div_row)
                except:
                    div_yield = 0  # 如果无法获取股息率，设为0
            
            # 应用格雷厄姆标准
            is_value = (pe < pe_threshold and pb < pb_threshold and div_yield > div_yield_threshold)
            
            # 打印调试信息
            if is_value:
                print(f"价值股: {stock_code}, PE: {pe:.2f}, PB: {pb:.2f}, 股息率: {div_yield:.2f}%")
            
            return is_value
        except Exception as e:
            print(f"判断股票{stock_code}是否为价值股时出错: {str(e)}")
            return False
    
    def filter_value_stocks(self, stocks_df, pe_threshold=15, pb_threshold=1.5, div_yield_threshold=4, market_cap_threshold=100):
        """从股票列表中筛选价值股"""
        if stocks_df.empty:
            return pd.DataFrame()
        
        # 预筛选：先按市值筛选
        try:
            if '总市值' in stocks_df.columns:
                # 将总市值转换为亿元
                stocks_df['市值(亿)'] = pd.to_numeric(stocks_df['总市值'], errors='coerce') / 100000000
                # 筛选市值大于阈值的股票
                pre_filtered = stocks_df[stocks_df['市值(亿)'] >= market_cap_threshold]
            else:
                pre_filtered = stocks_df
        except Exception as e:
            print(f"市值预筛选出错: {str(e)}")
            pre_filtered = stocks_df
        
        if pre_filtered.empty:
            return pd.DataFrame()
        
        print(f"市值预筛选后剩余{len(pre_filtered)}只股票")
        
        # 使用多线程并行处理
        value_stocks = []
        codes_to_check = pre_filtered['代码'].tolist()
        
        # 分批处理，每批50个股票
        batch_size = 50
        total_batches = (len(codes_to_check) + batch_size - 1) // batch_size
        
        for batch_idx in range(total_batches):
            start_idx = batch_idx * batch_size
            end_idx = min((batch_idx + 1) * batch_size, len(codes_to_check))
            batch_codes = codes_to_check[start_idx:end_idx]
            
            print(f"处理第{batch_idx+1}/{total_batches}批股票，共{len(batch_codes)}只...")
            
            # 创建线程任务
            futures = {}
            for code in batch_codes:
                row = pre_filtered[pre_filtered['代码'] == code].iloc[0]
                future = self.executor.submit(
                    self.check_value_stock, code, row, pe_threshold, pb_threshold, div_yield_threshold
                )
                futures[future] = code
            
            # 收集结果
            for future in as_completed(futures):
                code = futures[future]
                try:
                    result = future.result()
                    if result is not None:
                        value_stocks.append(result)
                except Exception as e:
                    print(f"处理股票{code}时出错: {str(e)}")
            
            # 防止请求过快
            if batch_idx < total_batches - 1:
                time.sleep(1)
        
        return pd.DataFrame(value_stocks) if value_stocks else pd.DataFrame()
    
    def get_low_suction_stocks(self, drop_min=2, drop_max=6, top_volume=200, 
                              pe_threshold=15, pb_threshold=1.5, div_yield_threshold=4, 
                              market_cap_threshold=100):
        """获取价值股中的低吸机会（跌幅在指定范围内）"""
        try:
            # 检查缓存
            cache_key = f"low_suction_{drop_min}_{drop_max}_{top_volume}_{pe_threshold}_{pb_threshold}_{div_yield_threshold}_{market_cap_threshold}"
            cached_result = self.cache.get_cache('analysis_result', cache_key)
            
            if cached_result is not None:
                print("使用缓存的分析结果")
                return cached_result, None
            
            # 获取成交额前N名的股票
            top_stocks = self.get_top_volume_stocks(top_volume)
            if top_stocks.empty:
                return pd.DataFrame(), "无法获取成交额前N名股票"
            
            print(f"成功获取成交额前{top_volume}名股票")
            
            # 筛选价值股
            value_stocks = self.filter_value_stocks(
                top_stocks, pe_threshold, pb_threshold, div_yield_threshold, market_cap_threshold
            )
            if value_stocks.empty:
                return pd.DataFrame(), "未找到符合格雷厄姆标准的价值股"
            
            print(f"成功筛选出{len(value_stocks)}只价值股")
            
            # 筛选跌幅在指定范围内的股票
            if '涨跌幅' not in value_stocks.columns:
                return pd.DataFrame(), "无法获取涨跌幅数据"
            
            # 将涨跌幅转换为数值类型
            value_stocks['涨跌幅'] = pd.to_numeric(value_stocks['涨跌幅'], errors='coerce')
            
            # 筛选跌幅在指定范围内的股票
            low_suction = value_stocks[
                (value_stocks['涨跌幅'] <= -drop_min) & 
                (value_stocks['涨跌幅'] >= -drop_max)
            ]
            
            if low_suction.empty:
                return pd.DataFrame(), f"未找到跌幅在{drop_min}%-{drop_max}%范围内的价值股"
            
            # 按跌幅排序（升序，跌幅越大排名越前）
            low_suction = low_suction.sort_values(by='涨跌幅', ascending=True)
            
            print(f"成功筛选出{len(low_suction)}只低吸机会股票")
            
            # 缓存结果
            self.cache.set_cache(low_suction, 'analysis_result', cache_key)
            
            return low_suction, None
        except Exception as e:
            error_msg = f"获取低吸机会股票时出错: {str(e)}"
            print(error_msg)
            return pd.DataFrame(), error_msg
    
    def format_value_stock_data(self, data):
        """格式化价值股数据用于显示"""
        if data.empty:
            return pd.DataFrame()
        
        # 选择要显示的列
        display_columns = [
            '代码', '名称', '最新价', '涨跌幅', '成交额', '总市值', 
            '市盈率', '市净率', '股息率'
        ]
        
        # 确保所有列都存在
        existing_columns = [col for col in display_columns if col in data.columns]
        formatted_data = data[existing_columns].copy()
        
        # 格式化列名
        column_names = {
            '代码': '股票代码',
            '名称': '股票名称',
            '最新价': '最新价(元)',
            '涨跌幅': '涨跌幅(%)',
            '成交额': '成交额(万元)',
            '总市值': '总市值(亿元)',
            '市盈率': 'PE(倍)',
            '市净率': 'PB(倍)',
            '股息率': '股息率(%)'
        }
        
        # 重命名列
        formatted_data = formatted_data.rename(columns=column_names)
        
        # 格式化数值
        if '涨跌幅(%)' in formatted_data.columns:
            formatted_data['涨跌幅(%)'] = formatted_data['涨跌幅(%)'].round(2)
        
        if '成交额(万元)' in formatted_data.columns:
            formatted_data['成交额(万元)'] = (formatted_data['成交额(万元)'] / 10000).round(2)
        
        if '总市值(亿元)' in formatted_data.columns:
            formatted_data['总市值(亿元)'] = (formatted_data['总市值(亿元)'] / 100000000).round(2)
        
        return formatted_data
